//+------------------------------------------------------------------+
//|                                      ManualTradeProtector_MT5.mq5 |
//|                                                                  |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024"
#property link      ""
#property version   "1.00"
#property strict

#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>

// Market state enumeration
enum ENUM_MARKET_STATE {
    MARKET_STATE_TRENDING_UP,
    MARKET_STATE_TRENDING_DOWN,
    MARKET_STATE_RANGING,
    MARKET_STATE_VOLATILE,
    MARKET_STATE_UNKNOWN
};

// Input parameters
input group "Risk Management Settings"
input double DailyLossLimit = 2.0;        // Daily Loss Limit (%)
input double MaxTotalRisk = 5.0;          // Maximum Total Risk (%)
input int    MaxPositions = 5;            // Maximum Number of Positions
input double RiskPerTrade = 1.0;          // Risk Per Trade (%)

input group "Take Profit Settings"
input bool   UseMultiLevelTP = true;      // Use Multi-Level Take Profit
input double TP1_Percent = 30.0;          // TP1 Percentage of Position
input double TP2_Percent = 30.0;          // TP2 Percentage of Position
input double TP3_Percent = 40.0;          // TP3 Percentage of Position
input double TP1_Reward = 1.5;            // TP1 Risk:Reward Ratio
input double TP2_Reward = 2.0;            // TP2 Risk:Reward Ratio
input double TP3_Reward = 3.0;            // TP3 Risk:Reward Ratio
input bool   UseTrailingStop = true;      // Use Trailing Stop
input int    TrailingStop = 50;           // Trailing Stop in Points

input group "Trend Analysis Settings"
input int    MAPeriod = 50;               // Moving Average Period
input int    TrendStrengthPeriod = 14;    // Trend Strength Period
input double MinTrendStrength = 0.6;      // Minimum Trend Strength
input int    RSIPeriod = 14;              // RSI Period
input int    BBPeriod = 20;               // Bollinger Bands Period
input double BBDeviation = 2.0;           // Bollinger Bands Deviation
input double VolatilityThreshold = 1.5;   // Volatility Threshold

// Global variables
CTrade g_trade;
CPositionInfo g_positionInfo;
double g_dailyLoss;
datetime g_lastResetTime;
double g_equityCurve[];
int g_equityCurveSize;
double g_maxDrawdown;
double g_currentDrawdown;
int g_maHandle;
int g_rsiHandle;
int g_bbHandle;
ENUM_MARKET_STATE g_marketState;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
    // Initialize trade object
    g_trade.SetExpertMagicNumber(123456);
    g_trade.SetMarginMode();
    g_trade.SetTypeFillingBySymbol(Symbol());
    g_trade.SetDeviationInPoints(10);
    
    // Initialize equity curve
    ArrayResize(g_equityCurve, 1000);
    g_equityCurveSize = 0;
    
    // Initialize indicators
    g_maHandle = iMA(Symbol(), PERIOD_CURRENT, MAPeriod, 0, MODE_SMA, PRICE_CLOSE);
    if(g_maHandle == INVALID_HANDLE) {
        Print("Failed to create MA indicator");
        return INIT_FAILED;
    }
    
    g_rsiHandle = iRSI(Symbol(), PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE);
    if(g_rsiHandle == INVALID_HANDLE) {
        Print("Failed to create RSI indicator");
        return INIT_FAILED;
    }
    
    g_bbHandle = iBands(Symbol(), PERIOD_CURRENT, BBPeriod, 0, BBDeviation, PRICE_CLOSE);
    if(g_bbHandle == INVALID_HANDLE) {
        Print("Failed to create Bollinger Bands indicator");
        return INIT_FAILED;
    }
    
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
    if(g_maHandle != INVALID_HANDLE) {
        IndicatorRelease(g_maHandle);
        g_maHandle = INVALID_HANDLE;
    }
    
    if(g_rsiHandle != INVALID_HANDLE) {
        IndicatorRelease(g_rsiHandle);
        g_rsiHandle = INVALID_HANDLE;
    }
    
    if(g_bbHandle != INVALID_HANDLE) {
        IndicatorRelease(g_bbHandle);
        g_bbHandle = INVALID_HANDLE;
    }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
    // Update risk management
    UpdateLossTracking();
    
    // Check if we can open new positions
    if(!CanOpenPosition()) return;
    
    // Check if market is suitable for trading
    if(!IsMarketSuitable()) return;
    
    // Process open positions
    ProcessOpenPositions();
}

//+------------------------------------------------------------------+
//| Update loss tracking                                             |
//+------------------------------------------------------------------+
void UpdateLossTracking() {
    datetime currentTime = TimeCurrent();
    
    // Reset daily loss if it's a new day
    MqlDateTime tm_now, tm_last;
    TimeToStruct(currentTime, tm_now);
    TimeToStruct(g_lastResetTime, tm_last);
    if (tm_now.day != tm_last.day) {
        g_dailyLoss = 0;
        g_lastResetTime = currentTime;
    }
    
    // Update daily loss
    double totalProfit = 0;
    for(int i = 0; i < PositionsTotal(); i++) {
        if(g_positionInfo.SelectByIndex(i)) {
            if(g_positionInfo.Symbol() == Symbol()) {
                totalProfit += g_positionInfo.Profit() + g_positionInfo.Swap();
            }
        }
    }
    
    g_dailyLoss = -totalProfit;
    
    // Update equity curve
    if(g_equityCurveSize < 1000) {
        g_equityCurve[g_equityCurveSize++] = AccountInfoDouble(ACCOUNT_EQUITY);
    }
    
    // Update drawdown
    UpdateDrawdown();
}

//+------------------------------------------------------------------+
//| Update drawdown tracking                                         |
//+------------------------------------------------------------------+
void UpdateDrawdown() {
    if(g_equityCurveSize < 2) return;
    
    double currentEquity = g_equityCurve[g_equityCurveSize - 1];
    double peakEquity = g_equityCurve[0];
    
    // Find peak equity
    for(int i = 1; i < g_equityCurveSize; i++) {
        if(g_equityCurve[i] > peakEquity) {
            peakEquity = g_equityCurve[i];
        }
    }
    
    // Calculate current drawdown
    if(peakEquity > 0) {
        g_currentDrawdown = (peakEquity - currentEquity) / peakEquity * 100;
        if(g_currentDrawdown > g_maxDrawdown) {
            g_maxDrawdown = g_currentDrawdown;
        }
    }
}

//+------------------------------------------------------------------+
//| Check if new position can be opened                              |
//+------------------------------------------------------------------+
bool CanOpenPosition() {
    // Check daily loss limit
    if(g_dailyLoss >= AccountInfoDouble(ACCOUNT_BALANCE) * DailyLossLimit / 100.0) {
        Print("Daily loss: ", g_dailyLoss);
        return false;
    }
    
    // Check total risk
    if(GetTotalRisk() >= AccountInfoDouble(ACCOUNT_BALANCE) * MaxTotalRisk / 100.0) {
        Print("Maximum total risk reached");
        return false;
    }
    
    // Check maximum positions
    if(PositionsTotal() >= MaxPositions) {
        Print("Maximum number of positions reached");
        return false;
    }
    
    return true;
}

//+------------------------------------------------------------------+
//| Get total risk                                                   |
//+------------------------------------------------------------------+
double GetTotalRisk() {
    double totalRisk = 0;
    
    for(int i = 0; i < PositionsTotal(); i++) {
        if(g_positionInfo.SelectByIndex(i)) {
            if(g_positionInfo.Symbol() == Symbol()) {
                totalRisk += g_positionInfo.Profit() + g_positionInfo.Swap();
            }
        }
    }
    
    return totalRisk;
}

//+------------------------------------------------------------------+
//| Calculate position size based on risk                            |
//+------------------------------------------------------------------+
double CalculatePositionSize(double stopLoss) {
    if(!CanOpenPosition()) return 0;
    
    double accountEquity = AccountInfoDouble(ACCOUNT_EQUITY);
    double riskAmount = accountEquity * RiskPerTrade / 100.0;
    
    // Get ATR for volatility-based position sizing
    int atrHandle = iATR(Symbol(), PERIOD_CURRENT, 14);
    if(atrHandle == INVALID_HANDLE) {
        Print("Failed to create ATR indicator");
        return 0;
    }
    
    double atrBuffer[];
    ArraySetAsSeries(atrBuffer, true);
    
    if(CopyBuffer(atrHandle, 0, 0, 1, atrBuffer) != 1) {
        Print("Failed to copy ATR buffer");
        IndicatorRelease(atrHandle);
        return 0;
    }
    
    double atr = atrBuffer[0];
    IndicatorRelease(atrHandle);
    
    // Calculate position size based on ATR
    double pointValue = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE);
    double stopLossPoints = MathAbs(stopLoss - SymbolInfoDouble(Symbol(), SYMBOL_BID)) / Point();
    
    if(stopLossPoints == 0) return 0;
    
    double positionSize = NormalizeDouble(riskAmount / (stopLossPoints * pointValue), 2);
    
    // Adjust position size based on volatility
    positionSize *= (1.0 - (atr / (atr * 2))); // Reduce size in high volatility
    
    return positionSize;
}

//+------------------------------------------------------------------+
//| Detect market state                                              |
//+------------------------------------------------------------------+
ENUM_MARKET_STATE DetectMarketState() {
    if(g_maHandle == INVALID_HANDLE || g_rsiHandle == INVALID_HANDLE || g_bbHandle == INVALID_HANDLE)
        return MARKET_STATE_UNKNOWN;
        
    // Get trend direction
    int trendDirection = GetTrendDirection();
    
    // Get RSI value
    double rsiValue = GetRSIValue(0);
    
    // Get Bollinger Bands values
    double bbUpper[], bbMiddle[], bbLower[];
    ArraySetAsSeries(bbUpper, true);
    ArraySetAsSeries(bbMiddle, true);
    ArraySetAsSeries(bbLower, true);
    
    bool bbCopySuccess = (CopyBuffer(g_bbHandle, 1, 0, 1, bbUpper) == 1 &&
                        CopyBuffer(g_bbHandle, 0, 0, 1, bbMiddle) == 1 &&
                        CopyBuffer(g_bbHandle, 2, 0, 1, bbLower) == 1);
                        
    if(!bbCopySuccess) {
        return MARKET_STATE_UNKNOWN;
    }
    
    // Calculate volatility
    double volatility = (bbUpper[0] - bbLower[0]) / bbMiddle[0];
    
    // Get current price
    double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    
    // Determine market state
    if(volatility > VolatilityThreshold) {
        g_marketState = MARKET_STATE_VOLATILE;
    }
    else if(trendDirection == 1 && rsiValue > 50) {
        g_marketState = MARKET_STATE_TRENDING_UP;
    }
    else if(trendDirection == -1 && rsiValue < 50) {
        g_marketState = MARKET_STATE_TRENDING_DOWN;
    }
    else if(currentPrice > bbLower[0] && currentPrice < bbUpper[0]) {
        g_marketState = MARKET_STATE_RANGING;
    }
    else {
        g_marketState = MARKET_STATE_UNKNOWN;
    }
    
    return g_marketState;
}

//+------------------------------------------------------------------+
//| Get trend direction                                              |
//+------------------------------------------------------------------+
int GetTrendDirection() {
    if(g_maHandle == INVALID_HANDLE) return 0;
    
    double maBuffer[];
    ArraySetAsSeries(maBuffer, true);
    
    if(CopyBuffer(g_maHandle, 0, 0, 2, maBuffer) != 2) {
        return 0;
    }
    
    if(maBuffer[0] > maBuffer[1]) return 1;    // Uptrend
    if(maBuffer[0] < maBuffer[1]) return -1;   // Downtrend
    return 0;                                  // No clear trend
}

//+------------------------------------------------------------------+
//| Get RSI value                                                    |
//+------------------------------------------------------------------+
double GetRSIValue(int shift) {
    if(g_rsiHandle == INVALID_HANDLE) return 0;
    
    double rsiBuffer[];
    ArraySetAsSeries(rsiBuffer, true);
    
    if(CopyBuffer(g_rsiHandle, 0, shift, 1, rsiBuffer) != 1) {
        return 0;
    }
    
    return rsiBuffer[0];
}

//+------------------------------------------------------------------+
//| Check if market is suitable for trading                          |
//+------------------------------------------------------------------+
bool IsMarketSuitable() {
    ENUM_MARKET_STATE state = DetectMarketState();
    
    // Don't trade in volatile or unknown market states
    if(state == MARKET_STATE_VOLATILE || state == MARKET_STATE_UNKNOWN)
        return false;
        
    return true;
}

//+------------------------------------------------------------------+
//| Process open positions                                           |
//+------------------------------------------------------------------+
void ProcessOpenPositions() {
    for(int i = 0; i < PositionsTotal(); i++) {
        if(g_positionInfo.SelectByIndex(i)) {
            if(g_positionInfo.Symbol() == Symbol()) {
                // Update trailing stop if enabled
                if(UseTrailingStop) {
                    UpdateTrailingStop(g_positionInfo.Ticket());
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Update trailing stop for a position                              |
//+------------------------------------------------------------------+
void UpdateTrailingStop(ulong ticket) {
    if(!g_positionInfo.Select(ticket)) return;
    
    double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    double currentSL = g_positionInfo.StopLoss();
    
    if(g_positionInfo.PositionType() == POSITION_TYPE_BUY) {
        double newSL = currentPrice - TrailingStop * Point();
        if(newSL > currentSL) {
            g_trade.PositionModify(ticket, newSL, g_positionInfo.TakeProfit());
        }
    }
    else if(g_positionInfo.PositionType() == POSITION_TYPE_SELL) {
        double newSL = currentPrice + TrailingStop * Point();
        if(newSL < currentSL || currentSL == 0) {
            g_trade.PositionModify(ticket, newSL, g_positionInfo.TakeProfit());
        }
    }
}

//+------------------------------------------------------------------+
//| Open a multi-level position                                      |
//+------------------------------------------------------------------+
bool OpenMultiLevelPosition(int direction, double stopLoss) {
    if(!CanOpenPosition()) return false;
    
    double positionSize = CalculatePositionSize(stopLoss);
    if(positionSize <= 0) return false;
    
    double entryPrice = (direction == 1) ? 
        SymbolInfoDouble(Symbol(), SYMBOL_ASK) : 
        SymbolInfoDouble(Symbol(), SYMBOL_BID);
    
    // Calculate take profit levels
    double tp1 = CalculateTakeProfit(direction, entryPrice, stopLoss, TP1_Reward);
    double tp2 = CalculateTakeProfit(direction, entryPrice, stopLoss, TP2_Reward);
    double tp3 = CalculateTakeProfit(direction, entryPrice, stopLoss, TP3_Reward);
    
    // Open positions with different take profit levels
    bool success = true;
    
    // TP1 position
    double size1 = NormalizeDouble(positionSize * TP1_Percent / 100.0, 2);
    if(size1 > 0) {
        if(direction == 1) {
            success &= g_trade.Buy(size1, Symbol(), 0, stopLoss, tp1);
        } else {
            success &= g_trade.Sell(size1, Symbol(), 0, stopLoss, tp1);
        }
    }
    
    // TP2 position
    double size2 = NormalizeDouble(positionSize * TP2_Percent / 100.0, 2);
    if(size2 > 0) {
        if(direction == 1) {
            success &= g_trade.Buy(size2, Symbol(), 0, stopLoss, tp2);
        } else {
            success &= g_trade.Sell(size2, Symbol(), 0, stopLoss, tp2);
        }
    }
    
    // TP3 position
    double size3 = NormalizeDouble(positionSize * TP3_Percent / 100.0, 2);
    if(size3 > 0) {
        if(direction == 1) {
            success &= g_trade.Buy(size3, Symbol(), 0, stopLoss, tp3);
        } else {
            success &= g_trade.Sell(size3, Symbol(), 0, stopLoss, tp3);
        }
    }
    
    return success;
}

//+------------------------------------------------------------------+
//| Calculate take profit level                                      |
//+------------------------------------------------------------------+
double CalculateTakeProfit(int direction, double entryPrice, double stopLoss, double rewardRatio) {
    double stopLossPoints = MathAbs(entryPrice - stopLoss);
    double takeProfitPoints = stopLossPoints * rewardRatio;
    
    if(direction == 1) {
        return entryPrice + takeProfitPoints;
    } else {
        return entryPrice - takeProfitPoints;
    }
} 